package com.lzy.xmlspring;

import org.w3c.dom.*;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * 解析xml文件的容器
 */
public class XmlApplicationContext {

	// 保存Bean对象的Map
	private ConcurrentMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();

	String xmlPath; // 需要解析的Spring配置文件

	public XmlApplicationContext(String xmlPath) {
		this.xmlPath = xmlPath;

		ClassLoader classLoader = XmlApplicationContext.class.getClassLoader();

/*		URL resource = classLoader.getResource("");  // 使用该种方式xml没有被加载到字节码中。
		// 获取项目字节码文件路径。后续xml中解析需要使用
		String clazzBasicPath = resource.getFile();*/

		String filePath = System.getProperty("user.dir")+"\\src";

		System.out.println(filePath);

		File files = new File(filePath);

		// 假设xml配置文件是唯一的,获取到xml配置文件
		File xmlConfigFile = recursionFindXmlByName(files, xmlPath);

		// 解析xml配置文件.
		documentGainBeanClassToMap(xmlConfigFile);
	}

	/**
	 * 根据配置文件名获取配置文件的真实路径
	 * @param files
	 * @param configPath
	 * @return
	 */
	public File recursionFindXmlByName(File files, String configPath) {
		File ans = null;
		if(files.isDirectory()) { // 是文件夹。
			File[] fileList = files.listFiles();
			for (File file : fileList) {
				ans = recursionFindXmlByName(file, configPath);
				if(null != ans) return ans;
			}
		} else { // 是文件
//			System.out.println(files.getName() + " ");
			if(files.getName().equals(configPath)) {
				return files;
			}
		}
		return ans;
	}


	/**
	 * 获取xml配置文件中的对象信息放入到Map中
	 * @param xmlConfigFile
	 */
	private void documentGainBeanClassToMap(File xmlConfigFile) {
		DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();

		documentBuilderFactory.setValidating(true); // 校验xml中Bean标签的定义是否符合连接的DTD规范

		try {
			DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();

			// 使用setErrorHandler()方法会将错误信息回调给我们
//			documentBuilder.setErrorHandler(new ErrorHandler() {......});

			// 注意这里如果解析错误发生提示的话不会阻止程序执行
			Document document = documentBuilder.parse(xmlConfigFile); // 获取xml文档对象

			System.out.println("xml解析完毕");

			// xml文档由元素，属性，文本等组成
			Element rootElement = document.getDocumentElement(); // 获取根元素

//			System.out.println(rootElement.getTagName()); // beans

			NodeList beanList = rootElement.getElementsByTagName("bean");

			for (int i = 0; i < beanList.getLength(); i++) {
				Node bean = beanList.item(i);
//				System.out.println(bean.getNodeName());  // bean

				// 获取Bean标签的属性对象 --> id，class
				NamedNodeMap beanAttrMap = bean.getAttributes();

				// 定义BeanDefinition对象进行接收class路径
				BeanDefinition beanDefinition = new BeanDefinition();

				String id = null;
				String classPath = null;

				for (int j = 0; j < beanAttrMap.getLength(); j++) {
					Node item = beanAttrMap.item(j);
//					System.out.println(item.getNodeName());  // 属性值
//					System.out.println(item.getNodeValue()); // 属性对象
					if("id".equals(item.getNodeName())) {
						id = item.getNodeValue();
					}

					if("class".equals(item.getNodeName())) {
						classPath = item.getNodeValue();
					}
				}

				// 简单处理一下。
				if(id == null || classPath == null || id.length() == 0 || classPath.length() == 0) throw new NullPointerException();

				String fullClassPath = classPath; // 类文件的真实路径需要为.eg: D:/program_workspace/...../target/../UserService.class。

//				System.out.println(fullClassPath); //当前输出的信息为： /D:/program_workspace/spring_implement/target/classes/com.lzy.xmlservice.OrderService.class

				// 填入Map中。
				Class clazz = this.getClass().getClassLoader().loadClass(fullClassPath);  // 需要的全限定名字为： com.lzy.xmlservice.UserService

				beanDefinition.setType(clazz);

				beanDefinitionMap.put(id, beanDefinition);
			}

		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	/**
	 * 根据再标签写入的Bean的id名字获取Bean对象
	 * @param beanNId
	 * @return
	 */
	public Object getBean(String beanNId) {
		BeanDefinition beanDefinition = beanDefinitionMap.get(beanNId);
		Class clazz = beanDefinition.getType();

		Object newInstance = null;
		try {
			newInstance = clazz.getConstructor().newInstance();
			return newInstance;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return newInstance;
	}
}